package com.huajin.codetest.util;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.Period;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalUnit;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.joda.time.Days;
import org.joda.time.Months;
import org.joda.time.Years;

/**
 * 时间日期工具流
 *
 * @author hongwei.lian
 * @date 2019年8月8日 下午12:43:38
 */
public class DateTimeUtil {
	
	private static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
	
	private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
	
	private static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
	
	/**
	 * 获取系统设置的默认时区
	 */
	private static final ZoneId ZONEID = ZoneId.systemDefault();
	
	private DateTimeUtil() {}
	
	/**
	 * 静态内部类对象=实例
	 *
	 * @author hongwei.lian
	 * @date 2019年8月8日 下午12:59:52
	 */
	private static class StaticSingletonHolder {
        private static final DateTimeUtil INSTANCE = new DateTimeUtil();
    }
	
	/**
	 * 获取实例
	 *
	 * @return 
	 * @author hongwei.lian
	 * @date 2019年8月8日 下午1:00:17
	 */
	public static DateTimeUtil getInstance() {
		return StaticSingletonHolder.INSTANCE;
	}
	
	/**
	 * 将LocalDateTime转换为指定的时间字符串
	 * @param localDateTime
	 * @param pattern
	 * @return
	 */
	public static String localDateTimeToDateString(LocalDateTime localDateTime, String pattern) {
		return localDateTimeToDateString(localDateTime, new String[] {pattern});
	}
	
	/**
	 * 将LocalDateTime转换为指定的时间字符串
	 *
	 * @param localDateTime
	 * @param pattern
	 * @return 
	 * @author hongwei.lian
	 * @date 2019年8月8日 下午1:00:25
	 */
	public static String localDateTimeToDateString(LocalDateTime localDateTime, String... pattern) {
		if (Objects.isNull(localDateTime)) {
			return "";
		}
		if (Objects.isNull(pattern) || Objects.equals(pattern.length, 0)) {
			pattern = new String[] {DEFAULT_DATE_TIME_FORMAT};
		}
		return DateTimeFormatter.ofPattern(pattern[0]).format(localDateTime);
	}
	
	public static String localDateToDateString(LocalDate localDate, String pattern) {
		return localDateToDateString(localDate, new String[] {pattern});
	}
	
	/**
	 * 将LocalDate转换为指定的时间字符串
	 *
	 * @param localDate
	 * @param pattern
	 * @return 
	 * @author hongwei.lian
	 * @date 2019年8月8日 下午1:00:57
	 */
	public static String localDateToDateString(LocalDate localDate, String... pattern) {
		if (Objects.isNull(localDate)) {
			return "";
		}
		if (Objects.isNull(pattern) || Objects.equals(pattern.length, 0)) {
			pattern = new String[] {DEFAULT_DATE_FORMAT};
		}
		return DateTimeFormatter.ofPattern(pattern[0]).format(localDate);
	}
	
	public static String localTimeToDateString(LocalTime localTime, String pattern) {
		return localTimeToDateString(localTime, new String[] {pattern});
	}
	
	/**
	 * 将LocalTime转换为指定的时间字符串
	 *
	 * @param localTime
	 * @param pattern
	 * @return 
	 * @author hongwei.lian
	 * @date 2019年8月8日 下午1:01:00
	 */
	public static String localTimeToDateString(LocalTime localTime, String... pattern) {
		if (Objects.isNull(localTime)) {
			return "";
		}
		if (Objects.isNull(pattern) || Objects.equals(pattern.length, 0)) {
			pattern = new String[] {DEFAULT_TIME_FORMAT};
		}
		return DateTimeFormatter.ofPattern(pattern[0]).format(localTime);
	}
	
	/**
	 * java.util.Date转换为java.time.LocalDateTime
	 *
	 * @param date
	 * @return 
	 * @author hongwei.lian
	 * @date 2019年8月1日 下午8:27:09
	 */
	public static LocalDateTime toLocalDateTime(Date date) {
		return Optional.ofNullable(date)
				                   .map(value -> LocalDateTime.ofInstant(value.toInstant(), ZONEID))
				                   .orElse(null);
	}

	/**
	 * java.time.LocalDateTime转换为 java.util.Date
	 *
	 * @param localDateTime
	 * @return 
	 * @author hongwei.lian
	 * @date 2019年8月30日 上午11:33:45
	 */
	public static Date toDate(LocalDateTime localDateTime) {
		return Optional.ofNullable(localDateTime)
					               .map(value -> Date.from(value.atZone(ZONEID).toInstant()))
					               .orElse(null);
	}
	
	/**
	 *  java.util.Date转换为java.time.LocalDateTime
	 *
	 * @param date
	 * @return 
	 * @author hongwei.lian
	 * @date 2019年10月24日 下午2:39:47
	 */
	public static LocalDateTime toLocalDateTime2(Date date) {
		return Optional.ofNullable(date)
				                   .map(value -> date.toInstant().atZone(ZONEID).toLocalDateTime())
				                   .orElse(null);
	}
	
	/**
	 * java.util.Date转换为java.time.LocalDate
	 *
	 * @param date
	 * @return 
	 * @author hongwei.lian
	 * @date 2019年10月24日 下午2:35:15
	 */
	public static LocalDate toLocalDate(Date date) {
		return Optional.ofNullable(date)
				                   .map(value -> date.toInstant().atZone(ZONEID).toLocalDate())
				                   .orElse(null);
	}
	
	/**
	 * java.util.Date转换为java.time.LocalTime
	 *
	 * @param date
	 * @return 
	 * @author hongwei.lian
	 * @date 2019年10月24日 下午2:35:57
	 */
	public static LocalTime toLocalTime(Date date) {
		return Optional.ofNullable(date)
				                   .map(value -> date.toInstant().atZone(ZONEID).toLocalTime())
				                   .orElse(null);
	}
	
	/**
	 * java.time.LocalDate转换为 java.util.Date
	 *
	 * @param localDate
	 * @return 
	 * @author hongwei.lian
	 * @date 2019年10月24日 下午2:41:32
	 */
	public static Date toDate(LocalDate localDate) {
		return Optional.ofNullable(localDate)
					               .map(value -> Date.from(value.atStartOfDay(ZONEID).toInstant()))
					               .orElse(null);
	}
	
	/**
	 * 获取指定日期所属月最大天数和指定天数的较小者
	 *
	 * @param localDate
	 * @param days
	 * @return
	 * @author hongwei.lian
	 * @date 2018年7月24日 下午12:06:50
	 */
	public static int getMinDays(LocalDate localDate, int days) {
		return days < localDate.lengthOfMonth() ? days : localDate.lengthOfMonth();
	}
	
	/**
	 * 使用指定天数设置指定日期
	 *
	 * @param localDate
	 * @param days
	 * @return
	 * @author hongwei.lian
	 * @date 2018年7月24日 下午12:07:57
	 */
	public static LocalDate setDays(LocalDate localDate, int days) {
		return LocalDate.of(localDate.getYear(), localDate.getMonth(), days);
	}
	
	/**
	 * 使用指定月数设置指定日期
	 *
	 * @param localDate
	 * @param months
	 * @return 
	 * @author hongwei.lian
	 * @date 2018年9月25日 下午6:07:52
	 */
	public static LocalDate setMonths(LocalDate localDate, int months) {
		return LocalDate.of(localDate.getYear(), months, localDate.getDayOfMonth());
	}
	
	/**
	 * 根据需求增加指定的日期
	 *
	 * @param localDate
	 * @param unit
	 * @param add
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年1月2日 下午6:08:58
	 */
	private static LocalDate plus(LocalDate localDate, TemporalUnit unit, int add) {
		return localDate.plus(add, unit);
	}
	
	/**
	 * 给指定日期增加天数
	 *
	 * @param localDate
	 * @param days
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年1月2日 下午6:08:35
	 */
	public static LocalDate addDays(LocalDate localDate, int days) {
		return plus(localDate, ChronoUnit.DAYS, days);
	}
	
	/**
	 * 给指定日期增加周数
	 *
	 * @param localDate
	 * @param weeks
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年1月2日 下午6:09:47
	 */
	public static LocalDate addWeeks(LocalDate localDate, int weeks) {
		return plus(localDate, ChronoUnit.WEEKS, weeks);
	}

	/**
	 * 给指定日期增加月数
	 *
	 * @param localDate
	 * @param months
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年1月2日 下午6:09:54
	 */
	public static LocalDate addMonths(LocalDate localDate, int months) {
		return plus(localDate, ChronoUnit.MONTHS, months);
	}

	/**
	 * 给指定日期增加年数
	 *
	 * @param localDate
	 * @param years
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年1月2日 下午6:10:00
	 */
	public static LocalDate addYears(LocalDate localDate, int years) {
		return plus(localDate, ChronoUnit.YEARS, years);
	}
	
	/**
	 * 根据需求增加指定的日期时间
	 *
	 * @param localDateTime
	 * @param unit
	 * @param add
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年1月2日 下午6:21:29
	 */
	private static LocalDateTime plus(LocalDateTime localDateTime, TemporalUnit unit, int add) {
		return localDateTime.plus(add, unit);
	}
	
	/**
	 * 给指定日期时间增加秒数
	 *
	 * @param localDateTime
	 * @param seconds
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年11月19日 下午8:52:07
	 */
	public static LocalDateTime addSeconds(LocalDateTime localDateTime, int seconds) {
		return plus(localDateTime, ChronoUnit.SECONDS, seconds);
	}
	
	/**
	 * 给指定日期时间增加分钟数
	 *
	 * @param localDateTime
	 * @param minutes
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年10月30日 下午12:39:26
	 */
	public static LocalDateTime addMinutes(LocalDateTime localDateTime, int minutes) {
		return plus(localDateTime, ChronoUnit.MINUTES, minutes);
	}
	
	/**
	 * 给指定日期时间增加小时数
	 *
	 * @param localDateTime
	 * @param hours
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年10月30日 下午12:39:26
	 */
	public static LocalDateTime addHours(LocalDateTime localDateTime, int hours) {
		return plus(localDateTime, ChronoUnit.HOURS, hours);
	}
	
	/**
	 * 给指定日期时间增加天数
	 *
	 * @param localDateTime
	 * @param days
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年1月2日 下午6:21:14
	 */
	public static LocalDateTime addDays(LocalDateTime localDateTime, int days) {
		return plus(localDateTime, ChronoUnit.DAYS, days);
	}
	
	/**
	 * 给指定日期时间增加周数
	 *
	 * @param localDateTime
	 * @param weeks
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年1月2日 下午6:20:55
	 */
	public static LocalDateTime addWeeks(LocalDateTime localDateTime, int weeks) {
		return plus(localDateTime, ChronoUnit.WEEKS, weeks);
	}

	/**
	 * 给指定日期时间增加月数
	 *
	 * @param localDateTime
	 * @param months
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年1月2日 下午6:20:21
	 */
	public static LocalDateTime addMonths(LocalDateTime localDateTime, int months) {
		return plus(localDateTime, ChronoUnit.MONTHS, months);
	}

	/**
	 * 给指定日期时间增加年数
	 *
	 * @param localDateTime
	 * @param years
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年1月2日 下午6:20:06
	 */
	public static LocalDateTime addYears(LocalDateTime localDateTime, int years) {
		return plus(localDateTime, ChronoUnit.YEARS, years);
	}
	
	/**
	 * 字符串转LocalDate
	 *
	 * @param dateString
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年8月29日 下午7:31:13
	 */
	public static LocalDate toLocalDate(String dateString) {
		return StringUtils.isBlank(dateString) ? null : LocalDate.parse(dateString, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT));
	}
	
	/**
	 * 字符串转LocalDateTime
	 *
	 * @param dateString
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年8月29日 下午7:31:17
	 */
	public static LocalDateTime toLocalDateTime(String dateString) {
		return StringUtils.isBlank(dateString) ? null : LocalDateTime.parse(dateString, DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT));
	}
	
	/**
	 * 字符串转LocalTime
	 *
	 * @param dateString
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年8月29日 下午7:31:21
	 */
	public static LocalTime toLocalTime(String dateString) {
		return StringUtils.isBlank(dateString) ? null : LocalTime.parse(dateString, DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT));
	}
	
	/**
	 * 计算两个日期时间的间隔
	 *
	 * @param startInclusive
	 * @param endExclusive
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年10月20日 下午2:44:24
	 */
	public static Duration between(Temporal startInclusive, Temporal endExclusive) {
		return Duration.between(startInclusive, endExclusive);
	}
	
	/**
	 * 计算两个日期时间的间隔秒数
	 *
	 * @param startInclusive
	 * @param endExclusive
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年10月20日 下午2:46:31
	 */
	public static long betweenSeconds(Temporal startInclusive, Temporal endExclusive) {
		return between(startInclusive, endExclusive).getSeconds();
	}
	
	/**
	 * 计算两个日期时间的间隔天数
	 * 只计算日期，未考虑年月
	 * @param startInclusive
	 * @param endExclusive
	 * @return 
	 * @author hongwei.lian
	 * @date 2020年10月20日 下午2:47:25
	 */
	public static long betweenDays(Temporal startInclusive, Temporal endExclusive) {
		return between(startInclusive, endExclusive).toDays();
	}
	
	/**
	 * 格式化日期为YYYY_MM_DD_23_59
	 * @author songhe.zhang
	 * @date 2020年11月13日 下午12:18:42
	 * @param localDateTime
	 * @return
	 */
	public static LocalDateTime localDateTimeFormatMaxTime(LocalDateTime localDateTime) {
		if(Objects.isNull(localDateTime)) {
			return localDateTime;
		}
		String date = localDateTime.toLocalDate().format(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT));
		if(StringUtils.isBlank(date)) {
			return localDateTime;
		}
		return LocalDateTime.parse(date + " 23:59:59", DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT));
	}
	
	/**
	 * 两个日期的相差实际总天数
	 * @param startLocalDate
	 * @param endLocalDate
	 * @return
	 * @author yandong.wang
	 * @date 2021年03月31日
	 */
	public static int betweenIntervalDays(LocalDate startLocalDate, LocalDate endLocalDate) {
		return (int)(endLocalDate.toEpochDay() - startLocalDate.toEpochDay());
	}
	
	/**
	 * 将日期按照指定的格式显示
	 * 
	 * @param date
	 * @param pattern
	 * @return
	 * @author hongwei.lian
	 * @date 2018年7月26日 下午4:48:04
	 */
	public static String formatDate(Date date, Object... pattern) {
		String formatDate = null;
		if (Objects.nonNull(pattern) && pattern.length > 0) {
			formatDate = DateFormatUtils.format(date, pattern[0].toString());
		} else {
			formatDate = DateFormatUtils.format(date, "yyyy-MM-dd");
		}
		return formatDate;
	}
	
	/**
	 * 获取两个日期间隔的天数
	 *
	 * @param startDate 起始日期
	 * @param endDate 终止日期
	 * @return
	 * @author hongwei.lian
	 * @date 2018年9月12日 下午6:15:09
	 */
	public static int daysBetweenDate(Date startDate, Date endDate){
		 return Days.daysBetween(new org.joda.time.LocalDate(startDate), new org.joda.time.LocalDate(endDate))
				             .getDays();
	 }
	
	/**
	 * 获取两个日期间隔的月数
	 *
	 * @param startDate 起始日期
	 * @param endDate 终止日期
	 * @return
	 * @author hongwei.lian
	 * @date 2018年9月13日 下午4:58:17
	 */
	public static int monthsBetweenDate(Date startDate, Date endDate){
		 return Months.monthsBetween(new org.joda.time.LocalDate(startDate), new org.joda.time.LocalDate(endDate))
	                             .getMonths();
	 }
	
	/**
	 * 获取两个日期间隔的年数
	 *
	 * @param startDate 起始日期
	 * @param endDate 终止日期
	 * @return
	 * @author hongwei.lian
	 * @date 2018年9月13日 下午4:58:40
	 */
	public static int yearsBetweenDate(Date startDate, Date endDate){
		 return Years.yearsBetween(new org.joda.time.LocalDate(startDate), new org.joda.time.LocalDate(endDate))
				              .getYears();
	 }
	
	/**
	 * 两个日期之间相差的年数（仅计算年之间间隔）
	 *
	 * @param startDateInclusive
	 * @param endDateExclusive
	 * @return
	 * @author hongwei.lian
	 * @date 2018年7月24日 上午9:54:22
	 */
	public static int betweenYearsOfTwoLocalDate(LocalDate startDateInclusive, LocalDate endDateExclusive) {
		return Period.between(startDateInclusive, endDateExclusive).getYears();
	}
	
	/**
	 * 两个日期之间相差的月数（仅计算月之间间隔）
	 *
	 * @param startDateInclusive
	 * @param endDateExclusive
	 * @return
	 * @author hongwei.lian
	 * @date 2018年7月24日 上午9:54:27
	 */
	public static int betweenMonthsOfTwoLocalDate(LocalDate startDateInclusive, LocalDate endDateExclusive) {
		return Period.between(startDateInclusive, endDateExclusive).getMonths();
	}
	
	/**
	 * 两个日期之间相差的天数（仅计算天之间间隔）
	 *
	 * @param startDateInclusive
	 * @param endDateExclusive
	 * @return
	 * @author hongwei.lian
	 * @date 2018年7月24日 上午9:54:31
	 */
	public static int betweenDaysOfTwoLocalDate(LocalDate startDateInclusive, LocalDate endDateExclusive) {
		return Period.between(startDateInclusive, endDateExclusive).getDays();
	}
	
	/**
	 * 取出指定月天数较小者
	 *
	 * @param startDateInclusive
	 * @param month
	 * @param settleInvestDay
	 * @return 
	 * @author hongwei.lian
	 * @date 2018年12月6日 下午2:42:17
	 */
	public static int getMinDays(LocalDate startDateInclusive, Month month, int settleInvestDay) {
		int lengthOfMonth = month.length(startDateInclusive.isLeapYear());
		return settleInvestDay > lengthOfMonth ? lengthOfMonth : settleInvestDay;
	}
	
	/**
	 * 获取今日剩余秒数
	 *
	 * @return 
	 * @author hongwei.lian
	 * @date 2021年8月19日 下午4:05:33
	 */
	public static long getTodayRemainingSeconds() {
		return DateTimeUtil.betweenSeconds(LocalDateTime.now(), 
				DateTimeUtil.toLocalDateTime(DateTimeUtil.localDateToDateString(
						DateTimeUtil.addDays(LocalDate.now(), 1), DEFAULT_DATE_FORMAT) + " 00:00:00"));
	}
	
}
